#pragma once

#include <assert.h>
#include <vector>
#include "angelscript.h"

template <typename T>
class vectorRegisterHelper
{
public:
	static void Construct(std::vector<T>* in)
	{
		new (in) std::vector<T>();
	}
			
	static void Destruct(std::vector<T>* in)
	{
		using namespace std;
		in->~vector<T>();
	}
	
	static void CopyConstruct(const std::vector<T>& rhs, std::vector<T>* in)
	{
		new (in) std::vector<T>(rhs);
	}

	static void NumConstruct(int size, std::vector<T>* in)
	{
		new (in) std::vector<T>(size);
	}

	static std::vector<T>& Assign(const std::vector<T>& rhs, std::vector<T>* lhs)
	{
		*lhs = rhs;
		return *lhs;
	}

	static T* Index(int i, std::vector<T>* lhs)
	{

#ifdef AS_VECTOR_ASSERTBOUNDS
		assert(i >= 0 && i < lhs->size() && "Array index out of bounds.");
#endif

#ifdef AS_VECTOR_CHECKBOUNDS
		if (i < 0 || i >= (signed)lhs->size())
		{
			asIScriptContext* context = asGetActiveContext();
			if( context )
				context->SetException("Array Index Out of Bounds.");
			return 0;
		}
#endif

		return &(*lhs)[i];
	}

	static int Size(std::vector<T>* lhs)
	{
		return (int)lhs->size();
	}

	static void Resize(int size, std::vector<T>* lhs)
	{
		lhs->resize(size);
	}

	static void PushBack(const T& in, std::vector<T> *lhs)
	{
		lhs->push_back(in);
	}

	static void PopBack(std::vector<T>* lhs)
	{
		lhs->pop_back();
	}

/*	static void Erase(int i, std::vector<T>* lhs)
	{
		lhs->erase(Index(i,lhs));
	}

	static void Insert(int i, const T& e, std::vector<T>* lhs)
	{
		lhs->insert(Index(i,lhs), e);
	}
*/
};


template <typename T>
bool RegisterVector(const std::string V_AS,  //The typename of the vector inside AS
	   		        const std::string T_AS,  //Template parameter typename in AS - must already be
			        asIScriptEngine* engine) //registered (or be primitive type)!!
{
	assert(engine && "Passed NULL engine pointer to registerVector");

	int error_code = 0;
	error_code = engine->RegisterObjectType(V_AS.c_str(), sizeof(std::vector<T>), asOBJ_CLASS_CDA);
	
	if (error_code < 0)
		return false;

	
	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(), 
		asBEHAVE_CONSTRUCT, 
		"void f()", 
		asFUNCTION(vectorRegisterHelper<T>::Construct), 
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_DESTRUCT,
		"void f()",
		asFUNCTION(vectorRegisterHelper<T>::Destruct),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_CONSTRUCT,
		(std::string("void f(")+V_AS+"&in)").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::CopyConstruct),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_CONSTRUCT,
		"void f(int)",
		asFUNCTION(vectorRegisterHelper<T>::NumConstruct),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;

	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_INDEX,
		(T_AS+"& f(int)").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::Index),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;

	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_INDEX,
		("const "+T_AS+"& f(int) const").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::Index),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectBehaviour(V_AS.c_str(),
		asBEHAVE_ASSIGNMENT,
		(V_AS+"& f(const "+V_AS+"&in)").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::Assign),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		"int length() const",
		asFUNCTION(vectorRegisterHelper<T>::Size),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		"void resize(int)",
		asFUNCTION(vectorRegisterHelper<T>::Resize),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		(std::string("void push_back(")+T_AS+"&in)").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::PushBack),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;
	
	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		"void pop_back()",
		asFUNCTION(vectorRegisterHelper<T>::PopBack),
		asCALL_CDECL_OBJLAST);
	if (error_code < 0)
		return false;

	return true;
/*	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		"void erase(int)",
		asFUNCTION(vectorRegisterHelper<T>::Erase),
		asCALL_CDECL_OBJLAST);
	assert(error_code == 0 && "Failed to register erase");

	error_code = engine->RegisterObjectMethod(V_AS.c_str(),
		(std::string("void insert(int, const ")+T_AS+"&)").c_str(),
		asFUNCTION(vectorRegisterHelper<T>::Insert),
		asCALL_CDECL_OBJLAST);
	assert(error_code == 0 && "Failed to register insert");
*/
}
